﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Jayden.Dll.Core.Persistence.Attributes;
using Jayden.Dll.Core.Persistence.Models;
using Jayden.Dll.Core.Persistence.Tools;

namespace Jayden.Dll.Core.Persistence.Data.SQLite
{
	public class SQLiteDataAccess<T> : DbDataAccess<T> where T : Object
	{
		protected static readonly SQLiteHelper Helper = new SQLiteHelper();
		private static bool m_CheckStructure = true;

		public SQLiteDataAccess(Configuration config, UnitOfWork uow, Entity entity) : base(config, uow, entity)
		{
			if (m_CheckStructure)
			{
				if (!TableExist())
					CreateTable();
				List<Field> fields = GetColumnsFromTable();
				List<Field> removed = new List<Field>();
				List<Property> added = new List<Property>();
				HashSet<string> fieldNameFound = new HashSet<string>();
				List<KeyValuePair<Property, Field>> mapped = new List<KeyValuePair<Property, Field>>();
				for (int c = 0; c < fields.Count; c++)
					if (!entity.Contains(fields[c].FieldName))
						removed.Add(fields[c]);
					else
					{
						mapped.Add(new KeyValuePair<Property, Field>(entity[fields[c].FieldName], fields[c]));
						fieldNameFound.Add(fields[c].FieldName);
					}

				foreach (Property p in entity.Properties)
					if (!fieldNameFound.Contains(p.Field.FieldName))
						added.Add(p);

				AlterTable(added, null, null);

				m_CheckStructure = false;
			}
		}

		protected List<Field> GetColumnsFromTable()
		{
			List<Field> fields = new List<Field>();
			string commandText = "PRAGMA table_info(" + Entity.Table.TableName + ")";//"SELECT * FROM sqlite_master WHERE type=@TableName";
			using (DbCommandProxy command = CreateCommand(commandText))
			{
				//CreateParameter(command, "TableName", Entity.Table.TableName);
				System.Data.DataTable dataTable = new System.Data.DataTable();
				using (DbDataReader reader = command.Command.ExecuteReader())
				{
					dataTable.Load(reader);
				}
				foreach (System.Data.DataRow row in dataTable.Rows)
				{
					Field field = new Field();
					field.FieldName = row["name"].ToString();
					field.Nullable = (row["notnull"] == (object)0);
					field.PrimaryKey = (row["pk"] == (object)1);
					fields.Add(field);
				}
			}
			return fields;
		}

		protected void AlterTable(List<Property> addedProperties, List<Field> removedFields, List<KeyValuePair<Field, Property>> modifiedFields)
		{
			foreach (Property property in addedProperties)
			{
				string alterCommandText = "ALTER TABLE " + Entity.Table.TableName + " ADD COLUMN " + Helper.GenerateField(property.Field, property.Entity.PrimaryKey);
				using (DbCommandProxy alterCommand = CreateCommand(alterCommandText))
					alterCommand.Command.ExecuteNonQuery();
			}
		}

		public bool TableExist()
		{
			using (DbCommandProxy tableExistCommand = CreateCommand(string.Format("SELECT name FROM sqlite_master WHERE name=@name")))
			{
				CreateParameter(tableExistCommand, "name", Entity.Table.TableName);
				using (DbDataReader rdr = tableExistCommand.Command.ExecuteReader())
				{
					if (rdr.HasRows)
						return true;
					else
						return false;
				}
			}
		}

		protected override System.Data.DbType GetType(object value)
		{
			return SQLiteHelper.GetDefaultDbType(value.GetType());
		}
		protected override void AssignLastGeneratedObjectId(T obj)
		{
			using (DbCommandProxy lastInsertedIdCommand = CreateCommand("SELECT last_insert_rowid()"))
			{
				long rowid = (long)lastInsertedIdCommand.Command.ExecuteScalar();
				obj.ObjectId[0] = rowid;
			}
		}

		protected override void AssignParameter(DbParameter parameter, object value)
		{
			Type type = value.GetType();
			if (value != null)
			{
				if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
					type = Nullable.GetUnderlyingType(type);
			}
			if (type == typeof(DateTime))
			{
				DateTime dt = (DateTime)value;
				long fullDateTime = 
					dt.Second +
					100 * dt.Minute +
					10000 * dt.Hour +
					1000000 * dt.Day +
					100000000 * dt.Month +
					10000000000 * dt.Year;
				parameter.Value = fullDateTime;
				parameter.DbType = DbType.Int64;
			}
			else
				base.AssignParameter(parameter, value);
		}
		protected override object ReadParameter(string parameterName, Type type, DbDataReader reader)
		{
			if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
				type = Nullable.GetUnderlyingType(type);
			if (type == typeof(DateTime))
			{
				object objectValue = reader[parameterName];
				if (objectValue == null || objectValue == DBNull.Value)
					return null;
				long value = (long)reader[parameterName];
				if (value < 10101010000)
					return default(DateTime);
				int second = (int)(value % 100);
				value /= 100;
				int minute = (int)(value % 100);
				value /= 100;
				int hour = (int)(value % 100);
				value /= 100;
				int day = (int)(value % 100);
				value /= 100;
				int month = (int)(value % 100);
				value /= 100;
				int year = (int)(value);
				return new DateTime(year, month, day, hour, minute, second);
			}
			return base.ReadParameter(parameterName, type, reader);
		}

		private string m_CreateCommandText;
		private string m_ReadCommandText;
		private string m_UpdateCommandText;
		private string m_DeleteCommandText;
		private string m_CreateTableCommandText;

		protected override string CreateCommandText
		{
			get
			{
				if (m_CreateCommandText == null)
					m_CreateCommandText = Helper.GenerateInsertInto(Entity);
				return m_CreateCommandText;
			}
		}

		protected override string ReadCommandText
		{
			get
			{
				if (m_ReadCommandText == null)
					m_ReadCommandText = Helper.GenerateSelect(Entity);
				return m_ReadCommandText;
			}
		}

		protected override string UpdateCommandText
		{
			get
			{
				if (m_UpdateCommandText == null)
					m_UpdateCommandText = Helper.GenerateUpdate(Entity);
				return m_UpdateCommandText;
			}
		}

		protected override string DeleteCommandText
		{
			get
			{
				if (m_DeleteCommandText == null)
					m_DeleteCommandText = Helper.GenerateDelete(Entity);
				return m_DeleteCommandText;
			}
		}

		protected override string CreateTableCommandText
		{
			get
			{
				if (m_CreateTableCommandText == null)
					m_CreateTableCommandText = Helper.GenerateCreateTable(Entity);
				return m_CreateTableCommandText;
			}
		}

		protected override string DropTableCommandText
		{
			get { return string.Format("DROP TABLE {0}", Entity.Table.TableName); }
		}
	}
}
